iT邦幫忙

2021 iThome 鐵人賽

DAY 23
0
Software Development

從零開始Reactive Programming- Spring系列 第 24

[Day 23] Reactive Programming - Spring WebFlux(Handler)

  • 分享至 

  • xImage
  •  

前言

經過上一個範例的練習,也大致上的知道相較於原本Spring MVC annotation-based,Spring WebFlux更傾向使用更Functional的Handler&Route,難道這樣就夠了嗎?
https://ithelp.ithome.com.tw/upload/images/20211007/201414189Fb3EVWXI4.png

圖片來源:網路

Handler

很顯然這樣的練習連小專案都不算,所以接下來介紹進階一點點,開始有了CRUD,常常戲稱CRUD工程師,就是要從CRUD學起。
首先下方有一個很基本的RestController,有GETPOST,有PathVariableRequestBody,最上方也有RequestMapping能夠統一路徑,這次就來把他改寫為Router + Handler模式。

@RestController 
@RequestMapping("/mvc/greeting") 
@RequiredArgsConstructor 
public class GreetingController { 
	private final GreetingRepository repository; 
	@GetMapping 
	public Flux<Greeting> allPeople() { 
		return this.repository.allGreeting(); 
	} 
	@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) 
	public Mono<Void> saveGreeting(@RequestBody Mono<Greeting> greetingMono) { 
		return this.repository.saveGreeting(greetingMono); 
	} 
	@GetMapping("/{id}") 
	public Mono<Greeting> getGreeting(@PathVariable int id) { 
		return this.repository.getGreeting(id); 
	} 
}

第一步先將邏輯的部份抽到Handler中,因為不能確定何時才會將資料回傳,所以回傳ServerResponse都會加上Reactive的Mono or Flux,Body裡面要提供type class是因為Flux的資料不是馬上就會存在,而是隨著時間傳入,也就是第一時間是沒辦法知道裡面的資料型態,所以我們要提前先指定好傳入。

 public Mono<ServerResponse> allGreeting(ServerRequest request) { 
    Flux<Greeting> greetingFlux = this.repository.allGreeting(); 
    return ServerResponse.ok().contentType(APPLICATION_JSON).body(greetingFlux, Greeting.class); 
  }

這邊的bodyToMono,就是之前spring mvc@RequestBody直接將Body轉成物件,這邊一樣預設是透過jackson

  public Mono<ServerResponse> saveGreeting(ServerRequest request) { 
    Mono<Greeting> greetingMono = request.bodyToMono(Greeting.class); 
    return ServerResponse.ok().build(this.repository.saveGreeting(greetingMono)); 
  }

@PathVariable取代掉,參考上面的結果很直覺就可以寫出下面的程式碼,但是當傳入一個不存在的ID,仍回傳兩百,如果想要回傳404則須調整作法,也就是現在會有兩種ServerResponse,一種正常回傳兩百,一種找不到回傳404,根據傳統的直覺你可就直接if else或是三元,但在Reactive的世界中,程式執行的當下很有可能是還沒有資料進來的,也就是永遠只回傳404,如果你停下來等待結果,則又走回了blocking的老路。

public Mono<ServerResponse> getGreeting(ServerRequest request) { 
    int id = Integer.parseInt(request.pathVariable("id")); 
    Mono<Greeting> greetingMono = this.repository.getGreeting(id);
    //Mono<ServerResponse> build = ServerResponse.notFound().build();
    return ServerResponse.ok().contentType(APPLICATION_JSON).body(greetingMono, Greeting.class);
  }

這時候Reactor有提供switchIfEmpty讓你可以很靈活很Functional的判斷。

  public Mono<ServerResponse> getGreeting(ServerRequest request) { 
    int id = Integer.parseInt(request.pathVariable("id")); 
    Mono<Greeting> greetingMono = this.repository.getGreeting(id); 
    return greetingMono 
        .flatMap(greeting -> ServerResponse.ok().contentType(APPLICATION_JSON).body( 
            BodyInserters.fromValue(greeting))) 
        .switchIfEmpty(ServerResponse.notFound().build()); 
  }

最後成果如下,下一篇來介紹Router

@Component
@RequiredArgsConstructor
public class GreetingHandler {

  private final GreetingRepository repository;

  public Mono<ServerResponse> hello(ServerRequest request) {
    return ServerResponse.ok().contentType(APPLICATION_JSON)
        .body(BodyInserters.fromValue(new Greeting("Hello, Spring!")));
  }
  public Mono<ServerResponse> getGreeting(ServerRequest request) {
    int id = Integer.parseInt(request.pathVariable("id"));
    Mono<Greeting> greetingMono = this.repository.getGreeting(id);
    return greetingMono
        .flatMap(greeting -> ServerResponse.ok().contentType(APPLICATION_JSON).body(
            BodyInserters.fromValue(greeting)))
        .switchIfEmpty(ServerResponse.notFound().build());
  }

  public Mono<ServerResponse> saveGreeting(ServerRequest request) {
    Mono<Greeting> greetingMono = request.bodyToMono(Greeting.class);
    return ServerResponse.ok().build(this.repository.saveGreeting(greetingMono));
  }

  public Mono<ServerResponse> allGreeting(ServerRequest request) {
    Flux<Greeting> greetingFlux = this.repository.allGreeting();
    return ServerResponse.ok().contentType(APPLICATION_JSON).body(greetingFlux, Greeting.class);
  }
}

結語

感覺得出來Spring想盡辦法降低原本使用Spring Mvc的開發者學習Spring WebFlux的門檻。

資料來源

上一篇
[Day 22] Reactive Programming - Spring WebFlux(Hello World) Part 2
下一篇
[Day 24] Reactive Programming - Spring WebFlux(Router)
系列文
從零開始Reactive Programming- Spring32
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言